package net.example.lesson6.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 引用测试.
 *
 * @author Yang Haikun
 * @since 0.0.1
 */
public class ReferenceDemo {
    /**
     * 设置JVM参数为-Xmx1m -Xms1m.
     * @param args args
     */
    public static void main(String[] args) {
        ReferenceDemo referenceDemo = new ReferenceDemo();
        //强引用
        //referenceDemo.strongReference();
        //软引用
        //referenceDemo.softReference();
        //弱引用
        //referenceDemo.weakReference();
        //虚引用
        referenceDemo.phantomReference();

    }

    /**
     * 测试强引用.
     * JVM宁愿抛出OOM也不会回收对象.
     */
    public void strongReference() {
        List<Demo> list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            Demo d = new Demo(i);
            list.add(d);
            System.out.println("demo is:" + d);
        }
    }


    /**
     * 软引用测试.
     * 当内存不足时,只有软引用指向的对象会被回收,并且添加进ReferenceQueue;
     */
    public void softReference() {
        final ReferenceQueue<Demo> queue = new ReferenceQueue<>();
        final List<SoftReference> list = new ArrayList<>();

        Runnable consumer = () -> {
            while (true) {
                Reference r = queue.poll();
                if (null != r) {
                    System.out.println("demo has been collected: " + r.toString() + "size of list is" + list.size());
                }
            }
        };

        new Thread(consumer).start();


        Runnable producer = () -> {
            for (int i = 0; i < 100000; i++) {
                SoftReference<Demo> sr = new SoftReference(new Demo(i), queue);
                list.add(sr);
                System.out.println("product demo: " + i + "size of list is" + list.size());

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        new Thread(producer).start();


    }

    /**
     * 弱引用测试.
     * 如果系统gc时一个对象只有弱引用指向，那么就会被回收.
     * 如果还有一个强引用指向该对象，那么该对象不会被回收
     */
    public void weakReference() {
        WeakReference<Demo> sr1 = new WeakReference<>(new Demo(1));
        WeakReference<Demo> sr2 = new WeakReference<>(new Demo(2));
        System.out.println("before gc demo1 is:" + sr1.get());
        System.out.println("before gc demo2 is:" + sr2.get());

        //使用一个强引用关联对象
        Demo d = sr2.get();

        System.gc();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("after gc demo1 is:" + sr1.get());
        System.out.println("after gc demo2 is:" + sr2.get());
    }

    /**
     * 虚引用测试.
     * 虚引用无法获取到对应的对象.
     * 当垃圾回收时，只有虚引用关联的对象一定会被回收，在回收前进入队列；
     */
    public void phantomReference() {
        final ReferenceQueue<Demo> queue = new ReferenceQueue<>();
        final List<PhantomReference> list = new ArrayList<>();
        PhantomReference<Demo> pr = new PhantomReference<>(new Demo(1), queue);
        System.out.println("is:" + pr.get());

        System.gc();

        try {
            Reference r = queue.remove();
            if (null != r) {
                System.out.println("PhantomReference has been collected");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public class Demo {
        private int id;
        private int[] data;

        public Demo(int id) {
            this.id = id;
            this.data = new int[1024 * 10];
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public int[] getData() {
            return data;
        }

        public void setData(int[] data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return this.getId() + "";
        }

    }
}
